---
title: SecureStore
description: A library that provides a way to encrypt and securely store key-value pairs locally on the device.
sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-secure-store'
packageName: 'expo-secure-store'
iconUrl: '/static/images/packages/expo-secure-store.png'
platforms: ['android', 'ios', 'tvos']
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';
import {
  ConfigPluginExample,
  ConfigReactNative,
  ConfigPluginProperties,
} from '~/ui/components/ConfigSection';
import { SnackInline } from '~/ui/components/Snippet';

`expo-secure-store` provides a way to encrypt and securely store key-value pairs locally on the device. Each Expo project has a separate storage system and has no access to the storage of other Expo projects.

**Large payloads can be rejected by the underlying platform. Historically, some iOS releases refused values above roughly 2048 bytes. Expo does not enforce a limit, so make sure to handle native errors if you plan to store very large strings.**

**The `requireAuthentication` option is not supported in Expo Go when biometric authentication is available due to a missing `NSFaceIDUsageDescription` key.**

## Installation

<APIInstallSection />

## Configuration in app config

You can configure `expo-secure-store` using its built-in [config plugin](/config-plugins/introduction/) if you use config plugins in your project ([Continuous Native Generation (CNG)](/workflow/continuous-native-generation/)). The plugin allows you to configure various properties that cannot be set at runtime and require building a new app binary to take effect. If your app does **not** use CNG, then you'll need to manually configure the library.

<ConfigPluginExample>

```json app.json
{
  "expo": {
    "plugins": [
      [
        "expo-secure-store",
        {
          "configureAndroidBackup": true,
          "faceIDPermission": "Allow $(PRODUCT_NAME) to access your Face ID biometric data."
        }
      ]
    ]
  }
}
```

</ConfigPluginExample>

<ConfigPluginProperties
  properties={[
    {
      name: 'configureAndroidBackup',
      platform: 'android',
      description:
        'A boolean indicating whether to configure automatic Android backup to work correctly with `expo-secure-store`. [Learn more](#android-auto-backup).',
      default: 'true',
    },
    {
      name: 'faceIDPermission',
      platform: 'ios',
      description: 'A string to set the [`NSFaceIDUsageDescription`](#ios) permission message.',
      default: '"Allow $(PRODUCT_NAME) to access your Face ID biometric data."',
    },
  ]}
/>

<ConfigReactNative>

Add `NSFaceIDUsageDescription` key to **Info.plist**:

```xml Info.plist
<key>NSFaceIDUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your Face ID biometric data.</string>
```

</ConfigReactNative>

## Platform value storage

### Android

On Android, values are stored in [`SharedPreferences`](https://developer.android.com/training/data-storage/shared-preferences), encrypted with [Android's Keystore system](https://developer.android.com/training/articles/keystore.html).

### iOS

On iOS, values are stored using the [keychain services](https://developer.apple.com/documentation/security/keychain_services) as `kSecClassGenericPassword`. **Due to the underlying nature of iOS Keychain, data stored with `expo-secure-store` will persist across app uninstallations when the app is reinstalled with the same bundle ID.** This is an expected behavior of the iOS Keychain system and should be considered when designing your app's data handling. iOS has the additional option of being able to set the value's `kSecAttrAccessible` attribute, which controls when the value is available to be fetched.

## Data persistence

`expo-secure-store` is designed to provide a persistent data storage solution across app restarts and updates. However, it is important not to rely on it as a single source of truth for irreplaceable, critical data.

- **On Android:** Data saved using `expo-secure-store` **will not be preserved upon app uninstallation**.
- **On iOS:** Data saved using `expo-secure-store` **will persist across app uninstallations** if the app is reinstalled with the same bundle ID. This is due to how the iOS Keychain manages stored credentials. Keep in mind that this is not guaranteed and you should never rely on this implementation detail.

Additionally, any data protected with the `requireAuthentication` option set to `true` will become inaccessible if there are changes to the user's biometric settings, such as adding a new fingerprint.

#### Exempting encryption prompt

Apple App Store Connect prompts you to select the type of encryption algorithm your app implements. This is known as **Export Compliance Information**. It is asked when publishing the app or submitting for TestFlight.

When using `expo-secure-store`, you can set the [`ios.config.usesNonExemptEncryption`](../config/app/#usesnonexemptencryption) property to `false` in the app config:

{/* prettier-ignore */}
```json app.json
{
  "expo": {
    "ios": {
      "config": {
        "usesNonExemptEncryption": false
      }
      /* @hide ... */ /* @end */
    }
  }
}
```

Setting this property automatically handles the compliance information prompt.

## Android Auto Backup

[Android Auto Backup for Apps](https://developer.android.com/identity/data/autobackup) automatically backs up a user's data from apps that target and run on Android 6.0 (API level 23) or higher.

The Auto Backup system has to be configured to exclude `expo-secure-store` shared preferences entries, as it's impossible to decrypt them after restoring the backup &mdash; app's entries are deleted from the Android Key Store when the app is uninstalled.

If your app doesn't have any custom backup configuration, `expo-secure-store` will automatically configure the Auto Backup system to ignore the `expo-secure-store` data.

If you are using your own Auto Backup configuration, you should exclude the `SecureStore` under the `sharedpref` domain and set the `configureAndroidBackup` to `false` in the [config plugin configuration](#example-appjson-with-config-plugin).

```xml
<!--  Auto Backup configuration for Android 12 and higher -->
<data-extraction-rules>
  <cloud-backup>
    <include domain="sharedpref" path="."/>
    <exclude domain="sharedpref" path="SecureStore"/>
  </cloud-backup>
  <device-transfer>
    <include domain="sharedpref" path="."/>
    <exclude domain="sharedpref" path="SecureStore"/>
  </device-transfer>
</data-extraction-rules>
```

```xml
<!--  Auto Backup configuration for Android 11 and lower -->
<full-backup-content>
  <include domain="sharedpref" path="."/>
  <exclude domain="sharedpref" path="SecureStore"/>
</full-backup-content>
```

## Usage

<SnackInline label='SecureStore' dependencies={['expo-secure-store']} platforms={['ios', 'android']}>

```jsx
import { useState } from 'react';
import { Text, View, StyleSheet, TextInput, Button } from 'react-native';
import * as SecureStore from 'expo-secure-store';

async function save(key, value) {
  await SecureStore.setItemAsync(key, value);
}

async function getValueFor(key) {
  let result = await SecureStore.getItemAsync(key);
  if (result) {
    alert("🔐 Here's your value 🔐 \n" + result);
  } else {
    alert('No values stored under that key.');
  }
}

export default function App() {
  const [key, onChangeKey] = useState('Your key here');
  const [value, onChangeValue] = useState('Your value here');

  return (
    <View style={styles.container}>
      <Text style={styles.paragraph}>Save an item, and grab it later!</Text>
      {/* @hide Add some TextInput components... */}

      <TextInput
        style={styles.textInput}
        clearTextOnFocus
        onChangeText={text => onChangeKey(text)}
        value={key}
      />
      <TextInput
        style={styles.textInput}
        clearTextOnFocus
        onChangeText={text => onChangeValue(text)}
        value={value}
      />
      {/* @end */}
      <Button
        title="Save this key/value pair"
        onPress={() => {
          save(key, value);
          onChangeKey('Your key here');
          onChangeValue('Your value here');
        }}
      />
      <Text style={styles.paragraph}>🔐 Enter your key 🔐</Text>
      <TextInput
        style={styles.textInput}
        onSubmitEditing={event => {
          getValueFor(event.nativeEvent.text);
        }}
        placeholder="Enter the key for the value you want to get"
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: 10,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  paragraph: {
    marginTop: 34,
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  textInput: {
    height: 35,
    borderColor: 'gray',
    borderWidth: 0.5,
    padding: 4,
  },
});
```

</SnackInline>

## API

```js
import * as SecureStore from 'expo-secure-store';
```

<APISection packageName="expo-secure-store" apiName="SecureStore" />
